package org.shangrila.app.sys.shiro;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.enterprise.context.SessionScoped;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.DefaultSubMenu;
import org.primefaces.model.menu.MenuModel;
import org.shangrila.app.sys.cfg.AppInitInfo;
import org.shangrila.app.sys.dao.SysDao;
import org.shangrila.app.sys.eo.SysAuthority;
import org.shangrila.app.sys.eo.SysLogin;
import org.shangrila.app.sys.eo.SysMenus;
import org.shangrila.app.sys.eo.SysResource;
import org.shangrila.app.sys.eo.SysRole;
import org.shangrila.app.sys.eo.SysUser;
import org.shangrila.comm.helper.IDHelper;
import org.shangrila.comm.security.ValidImageCreater;
import org.shangrila.comm.utils.MsgsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Named(value="appShiroValidate")
@SessionScoped
public class AppShiroValidate implements Serializable{

	private static final long serialVersionUID = 1L;
	private String infostr;
	private SysUser operatorUser;
	private String userno;
	private String username;
	private String password;
	private String validstr; // 验证图片上的字符文字:页面上使用
	private String validImgStr; // 验证图片上的字符文字: 后台使用	
	private StreamedContent pfimage;
	private List<SysRole> userRoles;
	private List<SysAuthority> userSysAuthorities;
	private List<String> shiroRoles;
	private List<String> shiroPermissions;
	private String[] operatorRoles;	
	private MenuModel model;
	
	private boolean loginFlag; // 标识用户是否已经登录，用于安全访问判断
	private boolean loginMode=true; // true为PC模式，false为手机模式

	private String loginPage = "/pages/login.jsf?faces-redirect=true";
	//private String loginPageView = "/pages/login.xhtml";
	private String homePage = "/pages/home.jsf?faces-redirect=true";
	private String mhomePage = "/pages/mhome.jsf?faces-redirect=true";

	private String ipaddress;
	private String userWxId;//微信用户Id
	
	public AppShiroValidate() {
		loginFlag = false;
		validstr = "";
		//createImage();
	}

	private static final transient Logger log = LoggerFactory.getLogger(AppShiroValidate.class);

	public String login()  { 
		if(logins()) {
			initMenuInfo();
			updateUser();//用户登录成功绑定微信号
			recordLoginInfo();
			if(loginMode) return homePage;
			else return mhomePage;
			}
		else
			return null;
	}
	
	@SuppressWarnings("unchecked")
	public boolean logins() throws FacesException {

		FacesContext fc = FacesContext.getCurrentInstance();
		ExternalContext ext = fc.getExternalContext();
		HttpServletRequest request = (HttpServletRequest) (ext.getRequest());
		ipaddress = request.getRemoteAddr();
		userWxId = (String) SecurityUtils.getSubject().getSession().getAttribute("userId");
		//判断是手机访问还是PC访问
		log.info("userAgent: " + request.getHeader("user-agent").toLowerCase());
		loginMode = !isMoblieAccess(request);		
		log.info("loginMode: " + loginMode);
		//log.info("userno:"+userno);
		//log.info("password:"+password);
		log.info("p2:"+AppEncryptService.encryptPassword(userno,password));
		
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(userno,AppEncryptService.encryptPassword(userno,password));
		try {			
			token.setRememberMe(true);
			subject.login(token);

			loginFlag = true;
			operatorUser = (SysUser)SecurityUtils.getSubject().getSession().getAttribute("shiroUser");			
			userRoles = (List<SysRole>)SecurityUtils.getSubject().getSession().getAttribute("userRoles");
			userSysAuthorities = (List<SysAuthority>)SecurityUtils.getSubject().getSession().getAttribute("userSysAuthorities");
			shiroRoles = (List<String>)SecurityUtils.getSubject().getSession().getAttribute("shiroRoles");
			shiroPermissions = (List<String>)SecurityUtils.getSubject().getSession().getAttribute("shiroPermissions");

			shiroRoles.add(userno);
			operatorRoles = new String[shiroRoles.size()];	
			shiroRoles.toArray(operatorRoles);

			// 取得当前loginFlag:"Y"
			String flag = (String)SecurityUtils.getSubject().getSession().getAttribute("loginFlag");	
			log.info("loginFlag:"+flag);
			
			return loginFlag;

		} catch  ( ExcessiveAttemptsException eae ) { 
			token.clear();
			MsgsUtil.showMessages("登录失败",	"超过三次尝试，不能登陆。");
			return loginFlag;
		} catch (UnknownAccountException ue) {
			token.clear();
			MsgsUtil.showMessages("登录失败",	"输入的账号不存在");
			return loginFlag;
		} catch (IncorrectCredentialsException ie) {
			token.clear();
			MsgsUtil.showMessages("登录失败",	"账号密码不匹配");
			return loginFlag;
		} catch (LockedAccountException lae) {
			MsgsUtil.showMessages("登录失败",	"账号锁定");
			return loginFlag;
		} catch (AuthenticationException ae) {
			MsgsUtil.showMessages("登录失败",	"认证错误");
			return loginFlag;
		} catch (RuntimeException re) {
			token.clear();
			//infostr = MsgsUtil.showMsgsInfo("info","MesShiroLoginValidate_login_runtime_error");
			MsgsUtil.showMessages("登录失败",	"认证错误");
			return loginFlag;
		}
		
	}
	
	public void initMenuInfo() {

		SysDao sysdao = new SysDao();
		//operatorUser = sysdao.findSysUserByUsernoPassword(userno, AppEncryptService.encryptPassword(userno,password));
		System.out.println(userno+" OASize="+operatorUser.getSysAuthorities().size());
		
		List<SysMenus> menuItems = sysdao.findSysMenuItems();//获取全部菜项信息 ID!=PID		
		List<SysMenus> menus = sysdao.findSysMenus(); //获取菜单（下面包含菜单项）ID=PID--->SubMenu
		
		List<SysMenus> validMenuItems  = new ArrayList<SysMenus>();//当前用户有效菜单项
		List<SysMenus> validMenus = new ArrayList<SysMenus>(); //当前用户有效菜单
				
		//过滤operatorUser的菜单项信息
		for (SysMenus ma : menuItems) {
			for (SysAuthority oa : operatorUser.getSysAuthorities()) {
				if(oa.getId() == ma.getSysAuthority().getId()){
					validMenuItems.add(ma);
				}
			}
		}
		//过滤operatorUser的菜单信息
		for (SysMenus ob : menus) {
			boolean flag = true;
			for (SysMenus mb : validMenuItems) {
				if( ob.getId() == mb.getPid()){
					flag = true;
					break;				
				}
				else
					flag = false;
			}
			if(flag) validMenus.add(ob);
		}

		model = new DefaultMenuModel();

		for (SysMenus m : validMenus) {//循环菜单
			DefaultSubMenu submenu = new DefaultSubMenu(m.getName());
			for (SysMenus mi : validMenuItems) {//循环菜单项
				if ( m.getId() == mi.getPid() ) {//如果菜单记录的PID与菜单的ID相等的是菜单项
					DefaultMenuItem item = new DefaultMenuItem(mi.getName());
					for (SysResource r : mi.getSysAuthority().getSysResources()) {//获取菜单项的URL和ICON
						if ("1".equalsIgnoreCase(r.getCategory())) {
							item.setUrl(r.getSource());
							item.setIcon(r.getIcon());
						}
					}
					submenu.addElement(item);
					}
			}
			model.addElement(submenu);
		}
	}
	
	public void recordLoginInfo(){
		
		SysLogin slog = new SysLogin();
		
		slog.setId(IDHelper.genNumID());
		slog.setLoginTime(new Date());
		slog.setUserno(getUserno());
		slog.setIpaddress(ipaddress);
		SysDao sysdao = new SysDao();
		try {
			sysdao.dbAccess.beginTransaction();
			sysdao.dbAccess.insert(slog);
			sysdao.dbAccess.commitTransaction();
		}catch(Exception e) {
			try{
				sysdao.dbAccess.rollback();
				}catch(Exception ex) {};
		}
		sysdao = null;
	}
	
	private void updateUser() {
		if(userWxId!=null && !userWxId.isEmpty()) {
			SysUser user;
			SysDao sysdao = new SysDao();
			try{
				user = (SysUser) sysdao.findSysUserByUsernoPassword(userno, AppEncryptService.encryptPassword(userno,password));
				if(user!=null) {
					user.setWxno(userWxId);
					sysdao.dbAccess.beginTransaction();
					sysdao.dbAccess.update(user);
					sysdao.dbAccess.commitTransaction();
				}else {
					log.info("user null");
				}
			}catch(Exception e){
				try{
					sysdao.dbAccess.rollback();
					}catch(Exception ex) {};
			}
			sysdao = null;
		}
		
	}

	public void validateUserno(FacesContext arg0, UIComponent arg1,
			Object arg3) throws ValidatorException {
		String content = (String) arg3;

		String regExp = "^[A-Za-z0-9]{3,16}$";
		if (!content.matches(regExp)) {
			throw new ValidatorException(MsgsUtil.showMessages("info",
					"请输入合法字符"));
		}
	}

	public void validatePassword(FacesContext arg0, UIComponent arg1,
			Object arg3) throws ValidatorException {

		String content = (String) arg3;

		// 判断字符串的内容:字母数字下划线以及特殊字符(~`!@#$%^&*()+-_=|:;/'\"<>,.?)组合,3-36个字符。
		// 考虑SQL注入风险，不能使用' " = | + - <> []
		String regExp = "^[A-Za-z0-9~`!@#$%^&*()_<>/\\{}:;,.?]{3,64}$";
		// String regExp = "^[A-Za-z0-9]{3,16}$";
		if (!content.matches(regExp)) {
			throw new ValidatorException(MsgsUtil.showMessages("info",
					"请输入合法字符"));
		}

	}

	public void validateImage(FacesContext arg0, UIComponent arg1, Object arg3)
			throws ValidatorException {
		String content = (String) arg3;
		if (content == null || !content.equalsIgnoreCase(validImgStr)) {
			throw new ValidatorException(MsgsUtil.showMessages("info",
					"请输入正确的验证字符"));
		}
	}

	public String refreshImage() {
		createImage();
		return loginPage;
	}

	public void createImage() {
		String fileName = AppInitInfo.VALIDATE_IMAGE_FILE_PATH;
		String filePath = FacesContext.getCurrentInstance()
				.getExternalContext().getRealPath("/");
		//log.info("filePath: "+ FacesContext.getCurrentInstance().getExternalContext().getRealPath("/"));

		fileName = filePath + fileName;
		ValidImageCreater vimage = new ValidImageCreater();
		validImgStr = vimage.createValidImage(fileName);
		pfimage = vimage.getPfimage();
		validstr = "";
		vimage = null;
		//log.info("fileName: " + fileName);
		log.info("validImgStr: " + validImgStr);
	}

	public static boolean isMoblieAccess(HttpServletRequest request) {
		boolean isMoblie = false;
		String[] mobileAgents = { "iphone", "android","ipad", "phone", "mobile", "wap", "netfront", "java", "opera mobi",
				"opera mini", "ucweb", "windows ce", "symbian", "series", "webos", "sony", "blackberry", "dopod",
				"nokia", "samsung", "palmsource", "xda", "pieplus", "meizu", "midp", "cldc", "motorola", "foma",
				"docomo", "up.browser", "up.link", "blazer", "helio", "hosin", "huawei", "novarra", "coolpad", "webos",
				"techfaith", "palmsource", "alcatel", "amoi", "ktouch", "nexian", "ericsson", "philips", "sagem",
				"wellcom", "bunjalloo", "maui", "smartphone", "iemobile", "spice", "bird", "zte-", "longcos",
				"pantech", "gionee", "portalmmm", "jig browser", "hiptop", "benq", "haier", "^lct", "320x320",
				"240x320", "176x220", "w3c ", "acs-", "alav", "alca", "amoi", "audi", "avan", "benq", "bird", "blac",
				"blaz", "brew", "cell", "cldc", "cmd-", "dang", "doco", "eric", "hipt", "inno", "ipaq", "java", "jigs",
				"kddi", "keji", "leno", "lg-c", "lg-d", "lg-g", "lge-", "maui", "maxo", "midp", "mits", "mmef", "mobi",
				"mot-", "moto", "mwbp", "nec-", "newt", "noki", "oper", "palm", "pana", "pant", "phil", "play", "port",
				"prox", "qwap", "sage", "sams", "sany", "sch-", "sec-", "send", "seri", "sgh-", "shar", "sie-", "siem",
				"smal", "smar", "sony", "sph-", "symb", "t-mo", "teli", "tim-", "tosh", "tsm-", "upg1", "upsi", "vk-v",
				"voda", "wap-", "wapa", "wapi", "wapp", "wapr", "webc", "winw", "winw", "xda", "xda-",
				"Googlebot-Mobile" };
		if (request.getHeader("User-Agent") != null) {
			String agent=request.getHeader("User-Agent").toLowerCase();
			for (String mobileAgent : mobileAgents) {
				if (agent.indexOf(mobileAgent) >= 0 && agent.indexOf("windows nt")<=0 && agent.indexOf("macintosh")<=0) {
					isMoblie = true;
					break;
				}
			}
		}
		return isMoblie;
	}
	
	public String getInfostr() {
		return infostr;
	}

	public void setInfostr(String infostr) {
		this.infostr = infostr;
	}

	public String getUsername() {
		if(operatorUser==null)
			username="";
		else
			username = operatorUser.getUsername();
		
		return username;
	}

	public SysUser getOperatorUser() {
		return operatorUser;
	}

	public void setOperatorUser(SysUser operatorUser) {
		this.operatorUser = operatorUser;
	}

	public List<SysRole> getUserRoles() {
		return userRoles;
	}

	public void setUserRoles(List<SysRole> userRoles) {
		this.userRoles = userRoles;
	}

	public List<SysAuthority> getUserSysAuthorities() {
		return userSysAuthorities;
	}

	public void setUserSysAuthorities(List<SysAuthority> userSysAuthorities) {
		this.userSysAuthorities = userSysAuthorities;
	}

	public List<String> getShiroRoles() {
		return shiroRoles;
	}

	public void setShiroRoles(List<String> shiroRoles) {
		this.shiroRoles = shiroRoles;
	}

	public List<String> getShiroPermissions() {
		return shiroPermissions;
	}

	public void setShiroPermissions(List<String> shiroPermissions) {
		this.shiroPermissions = shiroPermissions;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getValidstr() {
		return validstr;
	}

	public void setValidstr(String validstr) {
		this.validstr = validstr;
	}

	public boolean isLoginFlag() {
		return loginFlag;
	}

	public void setLoginFlag(boolean loginFlag) {
		this.loginFlag = loginFlag;
	}

	public StreamedContent getPfimage() {
		return pfimage;
	}

	public void setPfimage(StreamedContent pfimage) {
		this.pfimage = pfimage;
	}

	public String getUserno() {
		return userno;
	}

	public void setUserno(String userno) {
		this.userno = userno;
	}

	public String[] getOperatorRoles() {
		return operatorRoles;
	}

	public void setOperatorRoles(String[] operatorRoles) {
		this.operatorRoles = operatorRoles;
	}
	public MenuModel getModel() {
		return model;
	}

	public String getMhomePage() {
		return mhomePage;
	}

	public void setMhomePage(String mhomePage) {
		this.mhomePage = mhomePage;
	}

	public boolean isLoginMode() {
		return loginMode;
	}

	public void setLoginMode(boolean loginMode) {
		this.loginMode = loginMode;
	}
}